home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
ddj0190.arc
/
JAMES.LST
< prev
next >
Wrap
File List
|
1989-12-19
|
35KB
|
1,312 lines
_REAL-TIME ANIMATION_
by Rahner James
[LISTING ONE]
.model small, c
.286 ; This directive can be used to optimize procedure entry
; but not much else. I avoided all non-8088 commands
comment \
EGA Sprite Drivers for C
Copyright (c) February 1989, Ryu Consulting, Inc.
(916) 722 - 1939 anytime
Written by Rahner James, CS
This is a full functioning sprite driver for EGA graphics
adaptors. The sprite are given to the routines as a linked list
of sprite structures. All the function are re-entrant and can be
part of a multi-tasking system. The sprite structures are intended
to reside in far memory. The sprite can exist on any pixel boundary.
These were intended to be called from some C program, but probably
can be modified for some other language. This must be assembled with
Microsoft MASM version 5.0 or later since I make use of local
variables, forward/backward jumps and models. Expect to get some
incorrect size warnings because MASM doesn't seem to recognize its own
"byte ptr" and "word ptr" operators when used with words and dwords.
\
; ****************************************************************************
; EQUATES
; ****************************************************************************
EOI equ 20h ; End Of Interrupt signal
EOI_PORT equ 20h ; Port to output the EOI
CRT_MODE equ 49h
EGA_ADDRESS equ 63h
EGA_PIXELS_WORD equ 16 ; Number of pixels per word
EGA_PIXELS_BYTE equ 8 ; Number of pixels per byte
NUMBER_OF_PLANES equ 4 ; Number of EGA color planes
EGA_RETRACE_STATUS equ 3dah ; EGA retrace status register
RETRACE_BIT equ 1 shl 3 ; Bit set to signal a vertical retrace
SEQUENCE_REG equ 3c4h ; Sequencer register
GRAPHICS_12 equ 3ceh ; Graphics 1 & 2 register
MAP_MASK_REG equ 2 ; Map mask Indexed register
DATA_ROTATE_REG equ 3 ; Data Rotate Indexed register
DATA_OR equ 1 shl 4 ; Set to OR data on the EGA
DATA_MOVE equ 0 ; Write data unmodified onto EGA
BOTTOM_LINE equ 200 ; Lowest pixel line to allow a sprite
RIGHT_SIDE equ 640 ; Right-most visual pixel allowed
; ****************************************************************************
; STRUCTURES
; ****************************************************************************
; All sprites are stored on disk using the same internal format.
; The first word is the width of the sprites in bytes and the second
; word is the height of the sprite in widths. Each byte represents
; one pixel's worth of information. Bit 7 of the byte is the intensity
; bit, 0=off. This allows for 128 colors, black will be 0 or 80h. The
; intensity bit set indicates an opaque black surface. All color
; translations are table driven.
internal_sprite_structure struc ; Storage structure used for sprites
int_width dw ? ; Width in bytes for the sprite
int_height dw ? ; Height in widths of the sprite
int_body db ? ; Start of the sprite's body
internal_sprite_structure ends
ega_sprite_structure struc ; Internal EGA sprite structure
e_animate_ptr dw 0,0 ; Far ptr to next sprite struct in animation seq.
e_width dw ? ; Width of the sprite in words
e_height dw ? ; Height of sprite in widths
e_body dw ? ; Beginning of body
;.word 0: mask, word 1: sprite
;.throughout the body. That way you can pull
;.the background up, mask it, OR the sprite,
;.then store the background
; The body is organized into four planes,
;.termed PLANE0 to PLANE3. Each represents a
;.different color in the EGA spectrum, except
;.PLANE0 which is the intensity bit.
ega_sprite_structure ends
style_structure struc
style_width dw ? ; Width of each style entry in bytes
style_height dw ? ; Height of each style entry in pixels
style_body db ? ; Start of the style entries
style_structure ends
; ****************************************************************************
; LOCAL DATA STORAGE for DS
; ****************************************************************************
.data
public do_page_flip, done_page_flip
even
do_page_flip db 0 ; Set to -1 when non-visual page is completed
done_page_flip db -1 ; Set to -1 right after page has been swapped
flip_turn db 4 ; # of interrupts before an EGA page flip
old_irq_mask db 0 ; Old IRQ mask
EGA_settings db 2bh,2bh,2bh,2bh,24h,24h,23h,2eh
db 0,0,0,0,0,24h,23h,2eh,2bh
even
ega_base_port dw ?
default_retrace label word
v_retrace_reg db 11h
v_retrace_value db ?
.code
; ****************************************************************************
; LOCAL DATA STORAGE for CS
; ****************************************************************************
even
ega_segment dw 0a800h ; EGA page memory segment being set up
old_vector dw 0,0 ; Old IRQ-2 vector (as Checkov would say wecter)
; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
; EGA sprite related routines below this line
; void EGA_CONVERT( dest_ptr, source_ptr )
; Converts long storage format sprite into EGA structure sprite
; Given:
; dest_ptr far -> EGA sprite buffer ready to go
; source_ptr far -> disk sprite structure to convert
; Returns:
; EGA sprite buffer set up accordingly
ega_convert proc near uses di si ds, dest:dword, source:dword
local store_width:word, store_height:word
local source_width:word
local plane_size:word
cld
lds si, source ; DS:SI -> source sprite
les di, dest ; ES:DI -> CGA sprite buffer
mov es:[di], di ; Make the animate ptr point to itself
mov es:[di+2], es
add di, E_WIDTH
lodsw ; Get width in byte pixels
mov source_width, ax
add ax, EGA_PIXELS_WORD-1 ; Want to include those border pixels
mov cl, 4 ; Divide by sixteen to convert one byte
shr ax, cl ;.per pixel to 16 pixels/word for EGA
mov bx, ax ; Use this as our width count
stosw ; Store as words/pixels
lodsw ; Get height
mov store_height, ax
stosw ; Move height straight across
mul bx ; AX = Body size in mask/sprite entries
add ax, ax ; AX = body size in words
add ax, ax ; AX = plane size in bytes
mov plane_size, ax ; Save as our plane index
mov ax, ds ; Swap DS:SI and ES:DI
mov bx, es
mov ds, bx
mov es, ax
xchg di, si
sub si, 4 ; This is to prep for the next INC
next_row:
mov cx, source_width ; Get source row width
next_word:
add si, 4 ; SI -> next word in line
mov dx, -1 ; DX = the destination mask
xor ax, ax
mov [si], dx ; Set the mask word
mov [si+2], ax ; Clear the sprite word
mov bx, plane_size ; BX offset to next plane
mov [si+bx], dx ; Set the mask word
mov [si+bx+2], ax ; Clear the sprite word
add bx, plane_size ; BX offset to next plane
mov [si+bx], dx ; Set the mask word
mov [si+bx+2], ax ; Clear the sprite word
add bx, plane_size ; BX offset to next plane
mov [si+bx], dx ; Set the mask word
mov [si+bx+2], ax ; Clear the sprite word
mov dx, 1 shl 7 ; Start at MSB which is pixel LSB
next_pixel_byte:
jc next_word ; Only be set by pixel shift below
mov al, es:[di] ; Get the source pixel byte
inc di ; DI -> next source pixel byte
or al, al ; See if it's anything at all
jz end_pixel_byte ; Skip all the checks
xor [si], dx ; Reset the mask bit
test al, 1 shl 4 ; Check bit 7
jz @F ; Skip if nothing here
or [si+2], dx ; Place the sprite bit
@@: mov bx, plane_size ; BX -> plane 1 offset
xor [si+bx], dx ; Clear the mask bit
test al, 1 shl 7 ; Check bit 6
jz @F ; Skip if nothing here
or [si+bx+2], dx ; Place the sprite bit
@@: add bx, plane_size ; BX -> plane 2 offset
xor [si+bx], dx ; Clear the mask bit
test al, 1 shl 6 ; Check bit 5
jz @F ; Skip if nothing here
or [si+bx+2], dx ; Place the sprite bit
@@: add bx, plane_size ; BX -> plane 3 offset
xor [si+bx], dx ; Clear the mask bit
test al, 1 shl 5 ; Check bit 4
jz end_pixel_byte ; Skip if nothing here
or [si+bx+2], dx ; Place the sprite bit
end_pixel_byte:
shr dl, 1 ; Move pixel bit toward MS pixel bit
rcr dh, 1
loop next_pixel_byte ; Loop through the pixel bytes
dec store_height ; One less row
jnz next_row
ret
ega_convert endp
; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
; ui EGA_CALCULATE( source_ptr )
; Calculates the amount of storage needed for an unconverted sprite
; Given:
; source_ptr far -> disk sprite structure to convert
; Returns:
; AX = number of bytes needed to store the converted sprite
ega_calculate proc near uses si ds, source:dword
lds si, source ; DS:SI -> disk sprite structure for calculation
mov ax, [si] ; Get the width in bytes
add ax, EGA_PIXELS_WORD-1 ; Round up to nearest word
shr ax, 4 ; AX = number of words for stoarge
add ax, ax ; AX = number of bytes storage
add ax, ax ; AX = number of globs in one row
add ax, ax ; AX = number of row/planes
add ax, ax
mul word ptr [si].int_height ; AX = bytes per row * number of rows
add ax, E_BODY ; AX = body size + header size
ret
ega_calculate endp
; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
; void PUT_SPRITE( ui X, ui Y, ega_sprite_structure far *SPRITE_PTR )
; Maps a sprite list into the display video buffer
; Sprite is assumed to be 32-bits wide
; Given:
; X = X pixel location of sprite
; Y = Y pixel location of sprite
; SPRITE_PTR -> sprite structure to put on the screen
; Returns:
; The sprite is mapped onto the screen buffer
put_sprite proc uses di si, x:word, y:word, sprite_ptr:dword
local mask_msw:word ; OR'ed with the mask dword
local mask_lsw:word
local or_msw:word ; AND'ed with the sprite dword
local or_lsw:word
local start_di:word
local start_si:word
local sprite_plane_size:word
local number_of_rows:word
local shift:byte
local plane_number:byte
push ds
cmp y, 200 ; See if below 200
jc @F
short_done:
jmp done
@@: cmp x, 640
jnc short_done
cld
mov es, ega_segment
lds si, sprite_ptr ; DS:SI -> sprite to be driven
mov ax, 80 ; Calculate the offset
mul y
mov cx, x
mov di, cx
and cl, 7 ; CL = shift value
mov shift, cl
shr di, 3
add di, ax ; DI -> byte offset
mov start_di, di
mov ax, [si].e_height ; AX = height of the sprite in rows
mov number_of_rows, ax ; Save for later use
shl ax, 3 ; AX *= 8, to get size of 1 sprite plane
mov sprite_plane_size, ax
mov ax, number_of_rows ; Let's see if it goes too low
add ax, y
sub ax, BOTTOM_LINE
jbe @F ; Skip if it doesn't
sub number_of_rows, ax ; Update the number of rows
@@: add si, E_BODY ; SI -> start of sprite body
mov start_si, si
cmp x, 640-32 ; See if we are going to be right
ja short_mask ; Skip if no mask
cmp shift, 0
jnz shifted
mov bl, NUMBER_OF_PLANES
next_plane:
dec bl
mov dx, GRAPHICS_12 ; Talk to EGA control logic
mov al, 4
mov ah, bl
out dx, ax
mov dx, SEQUENCE_REG ; Set up the ports for writing as well
mov ax, 100h + MAP_MASK_REG
mov cl, bl
shl ah, cl
out dx, ax
mov cx, number_of_rows
@@: mov ax, es:[di] ; Get the background dword
mov dx, es:[di+2]
and ax, [si] ; Do the mask dword
and dx, [si+4]
or ax, [si+2] ; Bring on the sprite
or dx, [si+6]
mov es:[di], ax ; Replace with new graphic dword
mov es:[di+2], dx
add si, 8 ; Next row stuff
add di, 80
loop @B
mov di, start_di
mov si, start_si
add si, sprite_plane_size
mov start_si, si
or bl, bl
jnz next_plane
jmp done
short_mask:
jmp masked
shifted:
mov plane_number, NUMBER_OF_PLANES-1
mov cl, shift
mov bh, -1
shr bh, cl
next_shift_plane:
mov dx, GRAPHICS_12 ; Talk to EGA control logic
mov al, 4
mov ah, plane_number
out dx, ax
mov dx, SEQUENCE_REG ; Set up the ports for writing as well
mov ax, 100h + MAP_MASK_REG
mov cl, plane_number
shl ah, cl
out dx, ax
mov ch, byte ptr number_of_rows
mov cl, shift
@@: lodsw ; Get the sprite mask
xchg ah, al ; Switch them around
ror ax, cl
mov bl, ah ; Top CL bits are ones to mask 3rd byte
not bh ; BH = ~BH
and bl, bh
or ah, bh ; Now set the top ones of source byte
mov dx, es:[di] ; Get the first destination word
xchg ah, al ; Re-order the mask bytes
and dx, ax ; Mask it
lodsw ; Get the sprite
xchg ah, al
ror ax, cl
or dh, al ; OR DH w/ old AH
mov al, ah ; Save the upper bits
and ah, bh ; Mask off other bits
not bh ; BH = BH
and al, bh ; Cut out the rotunds
or dl, al ; OR least sig. bytes
mov es:[di], dx ; Save that first word, whew!
mov dl, ah ; DL = pushed up sprite bits
lodsw ; Get the next sprite mask
xchg ah, al ; Switch them around
ror ax, cl
mov dh, ah ; DH = MS shifted mask bits
and ah, bh ; Get rid of shifted bits
or ah, bl ; OR with shifted mask, previous byte
or dh, bh ; Add on the mask
xchg ah, al ; AH:AL back to normal
and es:[di+4], dh ; Easy way to get rid of DH
mov bl, dl ; BL = previous sprite bits
mov dx, es:[di+2] ; Get the destination word
and dx, ax ; Mask it
lodsw ; Get the sprite
xchg ah, al
ror ax, cl
or dh, al ; OR DH w/ old AH
mov al, ah ; Save the upper bits
not bh ; BH = ~BH
and ah, bh ; Mask off other bits
not bh ; BH = BH
and al, bh ; Cut out the rotunds
or al, bl
or dl, al ; OR least sig. bytes
mov es:[di+2], dx ; Save that first word, whew!
or es:[di+4], ah
add di, 80
dec ch
jnz @B
mov di, start_di
mov si, start_si
add si, sprite_plane_size
mov start_si, si
sub plane_number, 1
jc @F
jmp next_shift_plane
@@: jmp done
masked:
xor ax, ax ; Set up masks and ORs
mov mask_lsw, ax
mov mask_msw, ax
dec ax
mov or_lsw, ax
mov or_msw, ax
cmp x, 640-24 ; See if we have masked it already
jc @F ; Skip if we have
mov mask_msw, 0ff00h
mov or_msw, 0ffh
cmp x, 640-16
jc @F
mov mask_msw, -1 ; Make sure nothing gets masked
mov or_msw, 0
cmp x, 640-8 ; See if that's all
jc @F
mov mask_lsw, 0ff00h
mov or_lsw, 0ffh
@@: mov plane_number, NUMBER_OF_PLANES-1
next_mask_plane:
mov dx, GRAPHICS_12 ; Talk to EGA control logic
mov al, 4
mov ah, plane_number
out dx, ax
mov dx, SEQUENCE_REG ; Set up the ports for writing as well
mov ax, 100h + MAP_MASK_REG
mov cl, plane_number
shl ah, cl
out dx, ax
mov cx, number_of_rows
@@: mov ax, [si] ; Get the first mask
mov dx, [si+4] ; Get the second mask
or ax, mask_lsw
or dx, mask_msw
and ax, es:[di] ; Get the background dword
and dx, es:[di+2]
mov bx, or_lsw ; Get the OR lsw
and bx, [si+2] ; Bring on the sprite
or ax, bx
mov bx, or_msw
and bx, [si+6]
or dx, bx
mov es:[di], ax ; Replace with new graphic dword
mov es:[di+2], dx
add si, 8 ; Next row stuff
add di, 80
loop @B
mov di, start_di
mov si, start_si
add si, sprite_plane_size
mov start_si, si
sub plane_number, 1
jnc next_mask_plane
jmp done
done: pop ds
ret
put_sprite endp
; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
; void EGA_CLEAR_AREA( ui X1, ui Y1, ui X2, ui Y2 )
; Clears an area on the EGA display to black
; Given:
; X1,Y1 = X,Y pixel coordinates of the upper left corner
; X2,Y2 = X,Y pixel coordinates of the lower right corner
; Returns:
; Rectangular area from X1,Y1 to X2,Y2 (inclusive) cleared to black
public ega_clear_area
ega_clear_area proc uses ds si di, x1:word, y1:word, x2:word, y2:word
local height:word
local di_start:word
local word_columns:word
local di_offset:word
cld
cmp x1, 640 ; See if too far to the right
jc @F
mov x1, 0
@@: cmp x2, 640
jc @F
mov x2, 639
@@: mov ax, y2 ; Check out number of rows
sub ax, y1
jnc @F ; See if jerk put them in backwards
neg ax
@@: inc ax
mov bx, ax ; BX = number of rows
mov height, ax
mov ax, y1 ; Check our starting offset
mov cx, 80
mov di_offset, cx
mul cx
mov di, ax
mov ax, x1 ; See where we start
shr ax, 3
add di, ax
mov ax, x2
sub ax, x1
jnc @F
neg ax
@@: add ax, 16
shr ax, 4
jnz @F
jmp done
@@: mov word_columns, ax
add ax, ax
sub di_offset, ax
mov es, ega_segment
mov dx, SEQUENCE_REG ; Set up the ports for writing
mov ax, 0f00h+MAP_MASK_REG
out dx, ax
mov dx, di_offset
xor ax, ax
mov di_start, di
@@: mov cx, word_columns
rep stosw
add di, dx
dec bx
jnz @B
done: ret
ega_clear_area endp
; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
; void EGA_INSTALL()
; Installs IRQ-2 vectors for EGA card
; Given:
; nothing
; Returns:
; -1 if neither EGA or VGA else
; EGA interrupt vector installed and enabled
public ega_install
ega_install proc
mov ax, 40h ; ES -> video BIOS data area
mov es, ax
mov dx, es:[EGA_ADDRESS] ; DX -> CRTC Address port
mov ega_base_port, dx ; Save the port address
mov ax, 1a00h ; Read display combination
int 10h
cmp al, 1ah ; See if EGA
jne ega_adaptor
cmp bl, 7 ; See if VGA
je vga_adaptor
cmp bl, 8
je vga_adaptor
error_out:
mov ax, -1
jmp short done
ega_adaptor:
mov al, es:[CRT_MODE] ; AL = video BIOS mode number
mov bx, offset EGA_settings
xlat
jmp short @F
vga_adaptor:
mov al, v_retrace_reg ; AL = Vertical retrace register
out dx, al
inc dx
in al, dx
@@: mov v_retrace_value, al
mov done_page_flip, -1
mov do_page_flip, 0
xor ax, ax ; ES -> base page
mov es, ax
mov bx, 0ah*4 ; Vector for IRQ 2
mov dx, cs
mov ax, offset ega_interrupt
cli
xchg es:[bx], ax
xchg es:[bx+2], dx
mov old_vector, ax
mov old_vector+2, dx
in al, 21h ; Get present mask
mov old_irq_mask, al
and al, 11111011b
out 21h, al
mov dx, ega_base_port
mov ax, default_retrace
and ah, 11001111b
out dx, ax
jmp short $+2
or ah, 00010000b
out dx, ax
sti
done: ret
ega_install endp
; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
; void EGA_RIP_OUT()
; Undoes all the interrupt processing for the EGA
; Given:
; nothing
; Returns:
; EGA interrupt vector removed
public ega_rip_out
ega_rip_out proc uses es
mov ax, old_vector ; See if installed
or ax, old_vector+2
jz done ; Return if not installed
xor ax, ax ; ES -> base page
mov es, ax
mov bx, 0ah*4 ; Vector for IRQ 2
mov ax, old_vector
mov dx, old_vector+2
mov old_vector, 0
mov old_vector+2, 0
cli
mov es:[bx], ax
mov es:[bx+2], dx
in al, 21h
mov ah, old_irq_mask ; Restore old interrupt mask
and ah, 1 shl 2
and al, 11111011b
or al, ah
out 21h, al
mov dx, 3d4h
mov ax, 2b11h
out dx, ax
sti
done: ret
ega_rip_out endp
; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
; void EGA_INTERRUPT( void )
; Handles all the interrupt processing for the EGA controller
; Given:
; This is run at every IRQ-2 spike (ie. vertical retrace)
; Returns:
; If FLIP_TURN is brought to zero, EGA visual pages are swapped
; If swap is made, DO_PAGE_FLIP set to 0, DONE_PAGE_FLIP set to -1
ega_interrupt proc far
push ax
push dx
push ds
mov ax, @DATA
mov ds, ax
mov dx, 3c2h ; DX -> I/O port for input status
in al, dx
test al, 1 shl 7
jnz @F ; Interrupt is ours
pushf
call dword ptr [old_vector]
jmp done
@@: mov dx, ega_base_port ; DX -> EGA/VGA register port
in al, dx
push ax
mov ax, default_retrace
and ah, 11101111b
out dx, ax
jmp short $+2
mov al, EOI
out EOI_PORT, al
jmp short $+2
sti
dec flip_turn
jnz @F
mov flip_turn, 4
cmp do_page_flip, 0 ; See if we need to do this
jz @F
mov al, 0ch ; Select register for page MSB
mov ah, byte ptr ega_segment+1
shl ah, 4
out dx, ax ; Output the most significant byte
xor ega_segment, 800h ; Swap the active page
jmp short $+2
mov do_page_flip, 0
mov done_page_flip, -1
@@: cli
mov ax, default_retrace
and ah, 11011111b
or ah, 00010000b
out dx, ax
jmp short $+2
pop ax
out dx, al
done: pop ds
pop dx
pop ax
iret
ega_interrupt endp
end
[LISTING TWO]
comment \
Sprite Circle Handler
Copyright (c) February 1989, Ryu Consulting, Inc.
(916) 722 - 1939 anytime
Written by Rahner James, CS
\
; ****************************************************************************
; EQUATES
; ****************************************************************************
MAX_SPRITES equ 50
; ****************************************************************************
; STRUCTURES
; ****************************************************************************
sprite_structure struc
animate_ptr dw 0,0
sprite_width dw 0 ; Width in words
sprite_height dw 0 ; Height in pixels
sprite_body db ?
sprite_structure ends
struc
x dw 0
y dw 0
depth dw 0
pre_x dw 0
pre_y dw 0
dest_x dw 0
dest_y dw 0
adder_x dw 0
adder_y dw 0
sprite_ptr dw 0,0
next_node dw 0
sprite_node ends
.data
; ****************************************************************************
; DATA VARIABLES and EXTERNAL DEFINITIONS
; ****************************************************************************
extrn done_page_flip:byte, do_page_flip:byte
extrn min_x:word, min_y:word, max_y:word, max_x:word
first_sprite dw 0
public sprite_list
sprite_list sprite_node MAX_SPRITES dup(<>)
pre_max_x dw 639
pre_max_y dw 199
pre_min_x dw 0
pre_min_y dw 0
.code
; ****************************************************************************
; ROUTINES and EXTERNAL CODE DEFINITIONS
; ****************************************************************************
extrn do_background:near, put_sprite:near
; ****************************************************************************
; void DO_SPRITE_LIST( void )
; Sets up the sprites on the unviewed back page
; Given:
; A sprite list has been created and is stored in the array SPRITE_LIST
; Returns:
; If DONE_PAGE_FLIP is 0, no processing is done
; else all sprites in the sprite list are put on the non-visual page
; then DONE_PAGE_FLIP is set to 0 and DO_PAGE_FLIP is set to -1
do_sprite_list proc uses si
cmp done_page_flip, 0 ; See if we need to do this
jnz @F
jmp done
@@: call do_background
mov ax, pre_max_x
mov max_x, ax
mov ax, pre_max_y
mov max_y, ax
mov ax, pre_min_x
mov min_x, ax
mov ax, pre_min_y
mov min_y, ax
xor ax, ax ; Clear out some variables
mov pre_max_x, ax
mov pre_max_y, ax
dec ax
mov pre_min_x, ax
mov pre_min_y, ax
mov si, first_sprite ; SI -> sprite node to start with
next_sprite:
or si, si ; See if this is a NULL pointer
jnz @F
jmp almost_done
@@: mov ax, [si].x
mov [si].pre_x, ax
cmp [si].dest_x, ax ; See if we are already there
je no_add_x ; Skip if we are
mov cx, [si].dest_x ; Get our absolute value
sub cx, [si].x
jnc @F
neg cx
@@: add ax, [si].adder_x
mov [si].x, ax
sub ax, [si].dest_x
jnc @F
neg ax
@@: cmp cx, ax
jnc no_add_x
mov ax, [si].dest_x
mov [si].x, ax
no_add_x:
mov ax, [si].y
mov [si].pre_y, ax
cmp [si].dest_y, ax ; See if we are already there
je no_add_y ; Skip if we are
mov cx, [si].dest_y ; Get our absolute value
sub cx, [si].y
jnc @F
neg cx
@@: add ax, [si].adder_y
mov [si].y, ax
sub ax, [si].dest_y
jnc @F
neg ax
@@: cmp cx, ax
jnc no_add_y
mov ax, [si].dest_y
mov [si].y, ax
no_add_y:
cmp [si].y, 200 ; See if beyond bottom line
jnc pre_next_sprite
mov ax, [si].x ; See if we need to update some things
cmp ax, 640 ; See if beyond right column
jnc pre_next_sprite
cmp ax, pre_min_x ; See if pre_x < min_x
jnc @F ; Skip if not
mov pre_min_x, ax ; Update with new MIN_X
@@: mov ax, [si].y ; See if need to update min_y
cmp ax, pre_min_y
jnc @F
mov pre_min_y, ax
@@: les bx, dword ptr [si].sprite_ptr ; ES:BX -> sprite structure
mov ax, es:[bx].sprite_width
inc ax
if (@Cpu AND 2)
shl ax, 4 ; AX = AX * 16
else
rept 4
add ax, ax
endm
endif
add ax, [si].x
cmp pre_max_x, ax ; See if > max_x
jnc @F ; Jump if not
mov pre_max_x, ax ; Assume we are going to save it
@@: mov ax, es:[bx].sprite_height
add ax, [si].y
cmp pre_max_y, ax ; See if > max_x
jnc @F ; Jump if not
mov pre_max_y, ax ; Assume we are going to save it
@@: push es ; Set up for call to put_sprite()
push bx
push [si].y
push [si].x
call put_sprite
add sp, 4 ; Clear the stack
pop bx ; ES:BX -> sprite pointer
pop es
les bx, dword ptr es:[bx].animate_ptr
mov [si].sprite_ptr, bx
mov [si].sprite_ptr+2, es
pre_next_sprite:
mov si, [si].next_node ; SI -> next sprite node in line
jmp next_sprite
almost_done:
mov ax, pre_max_x
cmp ax, 640
jb @F
mov ax, 639
mov pre_max_x, ax
@@: cmp pre_min_x, ax
jb @F
dec ax
mov pre_min_x, ax
@@: mov ax, pre_max_y
cmp ax, 200
jb @F
mov ax, 199
mov pre_max_y, ax
@@: cmp pre_min_y, ax
jb @F
mov pre_min_y, ax
@@: mov done_page_flip, 0
mov do_page_flip, -1
done: ret
do_sprite_list endp
; ****************************************************************************
; void CLEAR_SPRITE_LIST( void )
; Zeros out the present sprite list
; Given:
; nothing
; Returns:
; FIRST_SPRITE and SPRITE_LIST array are zeroed
clear_sprite_list proc uses di
cld
mov di, offset sprite_list
mov ax, ds
mov es, ax
xor ax, ax
mov first_sprite, ax
mov cx, (MAX_SPRITES * (size sprite_node))/2
rep stosw
ret
clear_sprite_list endp
; ****************************************************************************
; int INSERT_SPRITE( ui X1,ui Y1, ui D_X,ui D_Y, ui PLUS_X,ui PLUS_Y,
; ui THE_DEPTH, sprite_structure far *SPRITE )
; Inserts the first sprite of a sprite circle into the linked list of circles
; Given:
; X1,Y1 = pixel location of the upper left corner of the sprite
; D_X,D_Y = pixel location of the destination of the sprite
; PLUS_X,PLUS_Y = pixels the sprite moves every page flip
; THE_DEPTH = apparent distance of the sprite from the viewer
; SPRITE -> sprite to insert
; Returns:
; If 0, sprite pointer was inserted in the array
; else !0 if there is no room
insert_sprite proc uses si, x1:word,y1:word, d_x:word,d_y:word,\
plus_x:word,plus_y:word, the_depth:word,\
sprite:dword
mov cx, MAX_SPRITES
mov bx, (offset sprite_list) - (size sprite_node)
@@: add bx, size sprite_node
mov ax, [bx].sprite_ptr ; See if this has been set yet
or ax, [bx].sprite_ptr+2
loopnz @B
jnz done_bad
les ax, sprite ; ES:AX -> sprite location
mov [bx].sprite_ptr, ax ; Save it
mov [bx].sprite_ptr+2, es
mov ax, x1 ; Set up the structure
mov [bx].x, ax
mov [bx].pre_x, ax
mov ax, y1
mov [bx].y, ax
mov [bx].pre_y, ax
mov ax, d_x
mov [bx].dest_x, ax
mov ax, d_y
mov [bx].dest_y, ax
mov ax, plus_x
mov [bx].adder_x, ax
mov ax, plus_y
mov [bx].adder_y, ax
mov ax, the_depth
mov [bx].depth, ax ; This is used in the following loop
mov dx, bx ; Save this s[rite entry for later
mov si, first_sprite ; SI -> sprite furthest from the viewer
mov bx, offset first_sprite ; BX -> previous sprite entry
mov cx, MAX_SPRITES
@@: or si, si ; See if it's a NULL ptr
jz @F ; Skip out if it is
cmp ax, [si].depth ; See if farther from observer
jnc @F
mov bx, si ; BX = this pointer
mov si, [si].next_node ; SI -> next sprite node in line
loop @B
done_bad:
mov ax, -1 ; Indicate we had a problem
jmp short done
@@: xchg si, dx ; SI -> sprite_list[i]
mov [si].next_node, dx
cmp bx, offset first_sprite
jne @F
mov [bx], si
jmp short done_good
@@: mov [bx].next_node, si
done_good:
xor ax, ax
done: ret
insert_sprite endp
; ****************************************************************************
; void ADD_SPRITE( sprite_structure far *DEST, far *SOURCE )
; Adds a self-relative sprite motion to the end of a sprite circle
; Given:
; DEST -> sprite circle header
; SOURCE -> sprite to add on
; Returns:
; SOURCE is added to the end of the sprite linked list and the
; circle ends are rejoined, may the circle be unbroken (ie Johnny Cash)
add_sprite proc uses si di ds, dest:dword, source:dword
lds si, dest
les di, source
next_sprite:
mov ax, [si].animate_ptr ; See if this is the end of the line
cmp ax, word ptr dest
jne @F
mov ax, [si].animate_ptr+2
cmp ax, word ptr dest+2
je got_the_end
@@: lds si, [si].animate_ptr
jmp next_sprite
got_the_end:
mov [si].animate_ptr, di
mov [si].animate_ptr+2, es
mov ax, word ptr dest
mov es:[di].animate_ptr, ax
mov ax, word ptr dest+2
mov es:[di].animate_ptr+2, ax
ret
add_sprite endp
end
[LISTING THREE]
/******************************************************************************
TITLE: SPRITES.C
Displays a sprite file on an EGA screen
Written by: Rahner James, CS
of Ryu Consulting, Inc.
******************************************************************************/
#include <stdio.h>
#include <dos.h>
#include <fcntl.h>
/******************************************************************************
VARIOUS DEFINITIONS
******************************************************************************/
#pragma pack(1)
typedef unsigned char uc;
typedef unsigned int ui;
typedef unsigned long ul;
/******************************************************************************
EXTERNAL DECLARATIONS
******************************************************************************/
extern void ega_convert();
extern ui ega_calculate( uc far * );
extern void ega_install();
extern void ega_clear_area( ui, ui, ui, ui );
/******************************************************************************
GLOBAL DATA
******************************************************************************/
ui min_x=0, min_y=0, max_x=639, max_y=199;
/******************************************************************************
long READ_ALL_FILE( uc *FILENAME, uc huge *BUFFER, ul BUFFER_SIZE )
Opens and reads an entire sprite file
Given:
FILENAME -> name of sprite file to read
BUFFER -> buffer to read the sprite file into
BUFFER_SIZE = number of bytes the buffer can hold
Returns:
File is opened, read and closed
Number of bytes read, if all went well
If error, returns -1
******************************************************************************/
long read_all_file( uc *filename, uc huge *buffer, ul buffer_size )
{
long rv = 0;
ui handle, dos_return, amount_read;
ui amount_to_read;
if ( _dos_open( filename, O_RDONLY, &handle ) )
return -1;
while ( buffer_size )
{
amount_to_read = buffer_size<60000L ? buffer_size : 60000L;
if ( _dos_read( handle, buffer+rv, amount_to_read, &amount_read ) )
{
rv = -1;
break;
}
rv += amount_read;
if ( amount_read < 60000 )
break;
buffer_size -= amount_read;
}
_dos_close( handle );
return rv;
}
/******************************************************************************
void DO_BACKGROUND( void )
Sets up the background for the sprite visual screen
Given:
nothing
Returns:
visual sprite screen erased
******************************************************************************/
void do_background( void )
{
ega_clear_area( min_x, min_y, max_x, max_y );
}
/******************************************************************************
uc SET_MODE( uc MODE_NUMBER )
Sets the video mode
Given:
Mode number to set video to
Returns:
Present video mode number
******************************************************************************/
uc set_mode( uc mode_number )
{
uc rv;
union REGS regs;
regs.h.ah = 15;
int86( 0x10, ®s, ®s );
rv = regs.h.al;
regs.h.ah = 0;
regs.h.al = mode_number;
int86( 0x10, ®s, ®s );
return rv;
}
/******************************************************************************
MAIN( int ARGC, uc *ARGV[] )
Allocates memory, reads in a sprite file, displays the sprites
until a key is pressed, frees up memory and interrupt vectors
Given:
ARGC = number of command line values, must be > 1
ARGV[1] -> file name of the sprite file to display
Returns:
0 if all went well, otherwise numbered according to error
******************************************************************************/
main( int argc, uc *argv[] )
{
ui i, x, y;
uc far *file_ptr, huge *sprite_start, huge *buffer_start;
uc huge *sprite_ptr[20];
uc old_mode;
ui memory_segment;
ul memory_size=0, file_size;
/* Check initial values and allocate memory for buffers */
if ( argc<2 )
{
printf( "\nNo file name has been given\n" );
exit( 1 );
}
if ( _dos_allocmem( -1, (ui *)&memory_size ) )
{
if ( _dos_allocmem( memory_size, &memory_segment ) )
{
printf( "\nMemory allocation error\n" );
exit( 2 );
}
}
else
{
memory_segment = memory_size;
memory_size = 0xffff;
}
memory_size <<= 4;
buffer_start = (uc huge *)((ul)memory_segment << 16L);
/* Read in the sprite file and then convert it to our intenal structure */
file_ptr = buffer_start;
if ( (file_size=read_all_file(argv[1],file_ptr,memory_size)) == -1 )
{
_dos_freemem( memory_segment );
printf( "\nGot error reading %s. Aborting.\n", argv[1] );
exit( 3 );
}
clear_sprite_list();
sprite_start = file_ptr + file_size;
for ( i=0 ; i<20 && file_size ; ++i )
{
ega_convert( sprite_ptr[i]=sprite_start, file_ptr );
x = ega_calculate( file_ptr );
sprite_start += x;
x = (ui)*file_ptr * (ui)*(file_ptr+2) + 4;
file_ptr += x;
if ( file_size > (ul)x )
file_size -= (ul)x;
else
file_size = 0;
}
/* Create linked list of sprite circles */
insert_sprite( 100,100, 100,100, 0,0, 7, sprite_ptr[0] );
for ( x=1 ; x<i ; ++x )
add_sprite( sprite_ptr[0], sprite_ptr[x] );
/* Setup the EGA screen mode and interrupt vector */
old_mode = set_mode( 0x10 );
ega_install();
/* Process the sprite list until someone taps a key */
while ( !kbhit() )
do_sprite_list();
/* Restore screen and allocated memory to original state */
ega_rip_out();
set_mode( old_mode );
_dos_freemem( memory_segment );
exit( 0 );
}
[LISTING FOUR]
sprite.obj: sprite.c
cl /c sprite.c
ega_drv.obj: ega_drv.asm
masm ega_drv;
gen_asm.obj: gen_asm.asm
masm gen_asm;
sprite.exe: sprite.obj ega_drv.obj gen_asm.obj
link sprite+gen_asm+ega_drv;
Examplσ 1║ Samplσ codσ fo≥ writinτ bi⌠ ma≡ t∩ EG┴ memory
MOV DX, 3C4h ; DX -> Sequencer register
MOV AL, 1 ; AL = index 1, Clocking Mode
OUT DX, AL
INC DX ; DX -> Sequencer index portèMOV AL, 5 ; AL = to put in Clocking Mode
OUT DX, AL
Examplσ 2║ Replacinτ thσ codσ iε Examplσ 1
MOV DX, 3C4h ; DX -> Sequencer reg. pair
MOV AX, 501h ; AL = index 1, AH = value 5èOUT DX, AX ; Puts AL out 3C4h, then
; AH out 3C5h